﻿using System;
using System.Collections.Generic;
using UnityEngine;


namespace Manox.Libs.Networking
{
    /// <summary>
    /// INetwork node.
    /// </summary>
    public interface INetworkNode
    {
        /// <summary>
        /// Send data.
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="data"></param>
        /// <param name="dataLength"></param>
        void Send(NetworkConnectionId connection, byte[] data, int dataLength);

        /// <summary>
        /// Register listener.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action"></param>        
        void RegisterListener<T>(Action<object, NetworkConnectionId, T> action) where T : NetworkMessage;

        /// <summary>
        /// Unregister listener.
        /// </summary>
        /// <param name="action"></param>
        void UnregisterListener<T>(Action<object, NetworkConnectionId, T> action) where T : NetworkMessage;        
    }

    /// <summary>
    /// Network node.
    /// </summary>
    public abstract class NetworkNode : MonoBehaviour, INetworkNode
    {
        /// <summary>Network listeners.</summary>
        private Dictionary<byte, List<INetworkAction>> NetworkListeners;


        /// <summary>
        /// Awake.
        /// </summary>
        public virtual void Awake()
        {
            this.NetworkListeners = new Dictionary<byte, List<INetworkAction>>();
        }

        /// <summary>
        /// Start.
        /// </summary>
        public virtual void Start()
        {
            Application.runInBackground = true;
        }

        /// <summary>
        /// On destroy.
        /// </summary>
        public virtual void OnDestroy()
        {
            this.NetworkListeners.Clear();
        }

        /// <summary>
        /// On enable.
        /// </summary>
        public virtual void OnEnable()
        {
        }

        /// <summary>
        /// On disable.
        /// </summary>
        public virtual void OnDisable()
        {
        }

        /// <summary>
        /// Update.
        /// </summary>
        public virtual void FixedUpdate()
        {

        }

        /// <summary>
        /// Send data.
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="data"></param>
        /// <param name="dataLength"></param>
        public virtual void Send(NetworkConnectionId connection, byte[] data, int dataLength)
        {

        }

        /// <summary>
        /// Register listener.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action"></param>
        public virtual void RegisterListener<T>(Action<object, NetworkConnectionId, T> action) where T : NetworkMessage
        {
            byte messageType = NetworkMessageTranslator.Instance.GetTypeId(typeof(T));

            if (messageType == 0)
                return;

            lock (this.NetworkListeners)
            {
                if (!this.NetworkListeners.ContainsKey(messageType))
                    this.NetworkListeners.Add(messageType, new List<INetworkAction>());

                NetworkAction<T> networkAction = new NetworkAction<T>(action);

                if (!this.NetworkListeners[messageType].Contains(networkAction))
                    this.NetworkListeners[messageType].Add(networkAction);
            }
        }

        /// <summary>
        /// Unregister listener.
        /// </summary>
        /// <param name="action"></param>
        public virtual void UnregisterListener<T>(Action<object, NetworkConnectionId, T> action) where T : NetworkMessage
        {
            byte messageType = NetworkMessageTranslator.Instance.GetTypeId(typeof(T));

            if (messageType == 0)
                return;

            lock (this.NetworkListeners)
                if (this.NetworkListeners.ContainsKey(messageType))
                {
                    NetworkAction<T> networkAction = new NetworkAction<T>(action);

                    if (this.NetworkListeners[messageType].Contains(networkAction))
                        this.NetworkListeners[messageType].Remove(networkAction);
                }
        }

        /// <summary>
        /// Send message to listeners.
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="messageType"></param>
        /// <param name="message"></param>
        public virtual void SendMessageToListeners(NetworkConnectionId connectionId, byte messageType, NetworkMessage message)
        {
            lock (this.NetworkListeners)
                if (this.NetworkListeners.ContainsKey(messageType))
                {
                    List<INetworkAction> listeners = this.NetworkListeners[messageType];

                    for (int i = listeners.Count - 1; i >= 0; i--)
                        listeners[i].ProcessData(this, connectionId, message);
                }
        }
    }

    /// <summary>
    /// Network response delegate.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="response"></param>
    public delegate void NetworkResponseDelegate<T>(T response);

    /// <summary>
    /// Connection changed delegate.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="connectionId"></param>
    public delegate void ConnectionChangedDelegate(object sender, NetworkConnectionId connectionId);
}
